home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-04 / pgp23src.zip / SRC / CONFIG.C < prev    next >
C/C++ Source or Header  |  1993-06-13  |  14KB  |  560 lines

  1. /*    config.c  - config file parser by Peter Gutmann
  2.     Parses config file for PGP
  3.  
  4.     Modified 24 Jun 92 - HAJK
  5.     Misc fixes for VAX C restrictions.
  6.  
  7. */
  8.  
  9. #include <ctype.h>
  10. #include <string.h>
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include "usuals.h"
  14. #include "fileio.h"
  15. #include "pgp.h"
  16. #include "config.h"
  17. #include "charset.h"
  18.  
  19. static int lookup( char *key, int keyLength, char *keyWords[], int range );
  20. static int extractToken( char *buffer, int *endIndex, int *length );
  21. static int getaString( char *buffer, int *endIndex );
  22. static int getAssignment( char *buffer, int *endIndex, INPUT_TYPE settingType );
  23. static void processAssignment( int intrinsicIndex );
  24.  
  25. /* The external config variables we can set here are referenced in pgp.h */
  26.  
  27. /* Return values */
  28.  
  29. #define ERROR    -1
  30. #define OK        0
  31.  
  32. /* The types of error we check for */
  33.  
  34. enum { NO_ERROR, ILLEGAL_CHAR_ERROR, LINELENGTH_ERROR };
  35.  
  36. #define CPM_EOF            0x1A    /* ^Z = CPM EOF char */
  37.  
  38. #define MAX_ERRORS        3        /* Max.no.errors before we give up */
  39.  
  40. #define LINEBUF_SIZE    100        /* Size of input buffer */
  41.  
  42. static int line;                /* The line on which an error occurred */
  43. static int errCount;            /* Total error count */
  44. static boolean hasError;        /* Whether this line has an error in it */
  45.  
  46. /* The settings parsed out by getAssignment() */
  47.  
  48. static char str[ LINEBUF_SIZE ];
  49. static int value;
  50. static boolean flag;
  51. static char *errtag;    /* prefix for printing error messages */
  52. static char optstr[100];    /* option being processed */
  53.  
  54. /* A .CFG file roughly follows the format used in the world-famous HPACK
  55.    archiver and is as follows:
  56.  
  57.     - Leading spaces/tabs (whitespace) are ignored.
  58.  
  59.     - Lines with a '#' as the first non-whitespace character are treated as
  60.       comment lines.
  61.  
  62.     - All other lines are treated as config options for the program.
  63.  
  64.     - Lines may be terminated by either linefeeds, carriage returns, or
  65.       carriage return/linefeed pairs (the latter being the DOS default
  66.       method of storing text files).
  67.  
  68.     - Config options have the form:
  69.  
  70.       <option> '=' <setting>
  71.  
  72.       where <setting> may be 'on', 'off', a numeric value, or a string
  73.       value.
  74.  
  75.     - If strings have spaces or the '#' character inside them they must be
  76.       surrounded by quote marks '"' */
  77.  
  78. /* Intrinsic variables */
  79.  
  80. #define NO_INTRINSICS        (sizeof(intrinsics) / sizeof(intrinsics[0]))
  81.  
  82. enum
  83. {    ARMOR, COMPRESS, SHOWPASS, KEEPBINARY, LANGUAGE,
  84.     MYNAME, TEXTMODE, TMP, TZFIX, VERBOSE, BAKRING,
  85.     ARMORLINES, COMPLETES_NEEDED, MARGINALS_NEEDED, PAGER,
  86.     CERT_DEPTH, CHARSET, CLEAR, SELF_ENCRYPT,
  87.     INTERACTIVE, PKCS_COMPAT,
  88.     /* options below this line can only be used as command line
  89.      * "long" options */
  90. #define CONFIG_INTRINSICS    BATCHMODE
  91.     BATCHMODE, FORCE
  92. };
  93.  
  94. static char *intrinsics[] =
  95. {    "ARMOR", "COMPRESS", "SHOWPASS", "KEEPBINARY", "LANGUAGE",
  96.     "MYNAME", "TEXTMODE", "TMP", "TZFIX", "VERBOSE", "BAKRING",
  97.     "ARMORLINES", "COMPLETES_NEEDED", "MARGINALS_NEEDED", "PAGER",
  98.     "CERT_DEPTH", "CHARSET", "CLEARSIG", "ENCRYPTTOSELF", 
  99.     "INTERACTIVE", "PKCS_COMPAT",
  100.     /* command line only */
  101.     "BATCHMODE", "FORCE",
  102. };
  103.  
  104. static INPUT_TYPE intrinsicType[] =
  105. {    BOOL, BOOL, BOOL, BOOL, STRING,
  106.     STRING, BOOL, STRING, NUMERIC, NUMERIC, STRING,
  107.     NUMERIC, NUMERIC, NUMERIC, STRING,
  108.     NUMERIC, STRING, BOOL, BOOL,
  109.     BOOL, NUMERIC,
  110.     /* command line only */
  111.     BOOL, BOOL,
  112. };
  113.  
  114. /* Possible settings for variables */
  115.  
  116. #define NO_SETTINGS            2
  117.  
  118. static char *settings[] = { "OFF", "ON" };
  119.  
  120.  
  121. /* Search a list of keywords for a match */
  122.  
  123. static int lookup( char *key, int keyLength, char *keyWords[], int range )
  124. {
  125.     int indx, pos = 0, matches = 0;
  126.  
  127.     strncpy(optstr, key, keyLength);
  128.     optstr[keyLength] = '\0';
  129.     /* Make the search case insensitive */
  130.     for( indx = 0; indx < keyLength; indx++ )
  131.         key[ indx ] = to_upper( key[ indx ] );
  132.  
  133.     for( indx = 0; indx < range; indx++ )
  134.         if( !strncmp( key, keyWords[ indx ], keyLength ) )
  135.         {    if (strlen(keyWords[indx]) == keyLength)
  136.                 return indx;    /* exact match */
  137.             pos = indx;
  138.             ++matches;
  139.         }
  140.     
  141.     switch (matches)
  142.     {    case 0: fprintf(stderr, "%s: unknown keyword: \"%s\"\n", errtag, optstr); break;
  143.         case 1: return pos;
  144.         default: fprintf(stderr, "%s: \"%s\" is ambiguous\n", errtag, optstr);
  145.     }
  146.     return ERROR;
  147. }
  148.  
  149. /* Extract a token from a buffer */
  150. static int extractToken( char *buffer, int *endIndex, int *length )
  151. {
  152.     int indx = 0, tokenStart;
  153.     char ch;
  154.  
  155.     /* Skip whitespace */
  156.     for( ch = buffer[ indx ]; ch && ( ch == ' ' || ch == '\t' ); ch = buffer[ indx ] )
  157.         indx++;
  158.     tokenStart = indx;
  159.  
  160.     /* Find end of setting */
  161.     while( indx < LINEBUF_SIZE && ( ch = buffer[ indx ] ) != '\0' && ch != ' ' && ch != '\t' )
  162.         indx++;
  163.     *endIndex += indx;
  164.     *length = indx - tokenStart;
  165.  
  166.     /* Return start position of token in buffer */
  167.     return( tokenStart );
  168. }
  169.  
  170.  
  171. /* Get a string constant */
  172. static int getaString( char *buffer, int *endIndex )
  173.     {
  174.     boolean noQuote = FALSE;
  175.     int stringIndex = 0, bufIndex = 1;
  176.     char ch = *buffer;
  177.  
  178.     /* Skip whitespace */
  179.     while( ch && ( ch == ' ' || ch == '\t' ) )
  180.         ch = buffer[ bufIndex++ ];
  181.  
  182.     /* Check for non-string */
  183.     if( ch != '\"' )
  184.         {
  185.         *endIndex += bufIndex;
  186.  
  187.         /* Check for special case of null string */
  188.         if( !ch )
  189.             {
  190.             *str = '\0';
  191.             return( OK );
  192.             }
  193.  
  194.         /* Use nasty non-rigorous string format */
  195.         noQuote = TRUE;
  196.         }
  197.  
  198.     /* Get first char of string */
  199.     if( !noQuote )
  200.         ch = buffer[ bufIndex++ ];
  201.  
  202.     /* Get string into string */
  203.     while( ch && ch != '\"' )
  204.         {
  205.         /* Exit on '#' if using non-rigorous format */
  206.         if( noQuote && ch == '#' )
  207.             break;
  208.  
  209.         str[ stringIndex++ ] = ch;
  210.         ch = buffer[ bufIndex++ ];
  211.         }
  212.  
  213.     /* If using the non-rigorous format, stomp trailing spaces */
  214.     if( noQuote )
  215.         while( stringIndex > 0 && str[ stringIndex - 1 ] == ' ' )
  216.             stringIndex--;
  217.  
  218.     str[ stringIndex++ ] = '\0';
  219.     *endIndex += bufIndex;
  220.  
  221.     /* Check for missing string terminator */
  222.     if( ch != '\"' && !noQuote )
  223.         {
  224.         if (line)
  225.             fprintf(stderr, "%s: unterminated string in line %d\n", errtag, line );
  226.         else
  227.             fprintf(stderr, "unterminated string: '\"%s'\n", str );
  228.         hasError = TRUE;
  229.         errCount++;
  230.         return( ERROR );
  231.         }
  232.  
  233.     return( OK );
  234.     }
  235.  
  236. /* Get an assignment to an intrinsic */
  237. static int getAssignment( char *buffer, int *endIndex, INPUT_TYPE settingType )
  238. {
  239.     int settingIndex = 0, length;
  240.  
  241.     buffer += extractToken( buffer, endIndex, &length );
  242.  
  243.     /* Check for an assignment operator */
  244.     if ( *buffer != '=' )
  245.     {
  246.         if (line)
  247.             fprintf(stderr, "%s: expected '=' in line %d\n", errtag, line );
  248.         else
  249.             fprintf(stderr, "%s: expected '=' after \"%s\"\n", errtag, optstr);
  250.         hasError = TRUE;
  251.         errCount++;
  252.         return( ERROR );
  253.     }
  254.     buffer++;        /* Skip '=' */
  255.  
  256.     buffer += extractToken( buffer, endIndex, &length );
  257.  
  258.     switch( settingType )
  259.     {
  260.         case BOOL:
  261.             /* Check for known intrinsic - really more general than just
  262.                checking for TRUE or FALSE */
  263.             if( ( settingIndex = lookup( buffer, length, settings, NO_SETTINGS ) ) == ERROR )
  264.             {
  265.                 hasError = TRUE;
  266.                 errCount++;
  267.                 return( ERROR );
  268.             }
  269.  
  270.             flag = ( settingIndex == 0 ) ? FALSE : TRUE;
  271.             break;
  272.  
  273.         case STRING:
  274.             /* Get a string */
  275.             getaString( buffer, &length );
  276.             break;
  277.  
  278.         case NUMERIC:
  279.             /* Get numeric input.  Error checking is a pain since atoi()
  280.                 has no real equivalent of NAN */
  281.             value = atoi( buffer );
  282.             break;
  283.     }
  284.  
  285.     return( settingIndex );
  286. }
  287.  
  288. /* Process an assignment */
  289.  
  290. static void processAssignment( int intrinsicIndex )
  291.     {
  292.     if( !hasError )
  293.         switch( intrinsicIndex )
  294.             {
  295.             case ARMOR:
  296.                 emit_radix_64 = flag;
  297.                 break;
  298.  
  299.             case COMPRESS:
  300.                 compress_enabled = flag;
  301.                 break;
  302.  
  303.             case SHOWPASS:
  304.                 showpass = flag;
  305.                 break;
  306.  
  307.             case KEEPBINARY:
  308.                 keepctx = flag;
  309.                 break;
  310.  
  311.             case LANGUAGE:
  312.                 strncpy(language, str, 15);
  313.                 break;
  314.  
  315.             case BAKRING:
  316.                 strcpy(floppyring, str);
  317.                 break;
  318.  
  319.             case MYNAME:
  320.                 strcpy(my_name, str);
  321.                 break;
  322.  
  323.             case TEXTMODE:
  324.                 if( flag )
  325.                     literal_mode = MODE_TEXT;
  326.                 else
  327.                     literal_mode = MODE_BINARY;
  328.                 break;
  329.  
  330.             case TMP:
  331.                 /* directory pathname to store temp files */
  332.                 settmpdir(str);
  333.                 break;
  334.  
  335.             case TZFIX:
  336.                 /* How many hours to add to time() to get GMT. */
  337.                 /* Compute seconds from hours to shift to GMT: */
  338.                 timeshift = 3600L * (long) value;
  339.                 break;
  340.  
  341.             case VERBOSE:
  342.                 switch (value)
  343.                 {
  344.                     case 0: quietmode = TRUE; verbose = FALSE; break;
  345.                     case 1: quietmode = FALSE; verbose = FALSE; break;
  346.                     case 2: quietmode = FALSE; verbose = TRUE; break;
  347.                     default: quietmode = FALSE; verbose = FALSE;
  348.                 }
  349.                 break;
  350.  
  351.             case ARMORLINES:
  352.                 pem_lines = value;
  353.                 break;
  354.  
  355.             case MARGINALS_NEEDED:
  356.                 marg_min = value;
  357.                 if (marg_min < 1)
  358.                     marg_min = 1;
  359.                 break;
  360.  
  361.             case COMPLETES_NEEDED:
  362.                 compl_min = value;
  363.                 if (compl_min < 1)
  364.                     compl_min = 1;
  365.                 if (compl_min > 4)
  366.                     compl_min = 4;
  367.                 break;
  368.  
  369.             case CERT_DEPTH:
  370.                 max_cert_depth = value;
  371.                 if (max_cert_depth < 0)
  372.                     max_cert_depth = 0;
  373.                 if (max_cert_depth > 8)
  374.                     max_cert_depth = 8;
  375.                 break;
  376.  
  377.             case PAGER:
  378.                 strcpy(pager, str);
  379.                 break;
  380.  
  381.             case CHARSET:
  382.                 strcpy(charset, str);
  383.                 break;
  384.  
  385.             case CLEAR:
  386.                 clear_signatures = flag;
  387.                 break;
  388.                 
  389.             case SELF_ENCRYPT:
  390.                 encrypt_to_self = flag;
  391.                 break;
  392.                 
  393.             case INTERACTIVE:
  394.                 interactive_add = flag;
  395.                 break;
  396.                 
  397.             case BATCHMODE: batchmode = flag; break;
  398.             case FORCE: force_flag = flag; break;
  399.             case PKCS_COMPAT: pkcs_compat = value; break;
  400.             }
  401.     }
  402.  
  403. /* Process an option on a line by itself.  This expects options which are
  404.    taken from the command-line, and is less finicky about errors than the
  405.    config-file version */
  406.  
  407. int processConfigLine( char *option )
  408.     {
  409.     int indx, intrinsicIndex;
  410.     char ch;
  411.  
  412.     /* Give it a pseudo-linenumber of 0 */
  413.     line = 0;
  414.  
  415.     errtag = "pgp";
  416.     errCount = 0;
  417.     for( indx = 0;
  418.          indx < LINEBUF_SIZE && ( ch = option[ indx ] ) != '\0' &&
  419.                 ch != ' ' && ch != '\t' && ch != '=';
  420.          indx++ );
  421.     if( ( intrinsicIndex = lookup( ( char * ) option, indx, intrinsics, NO_INTRINSICS ) ) == ERROR )
  422.         return -1;
  423.     if (option[indx] == '\0' && intrinsicType[intrinsicIndex] == BOOL)
  424.     {    /* boolean option, no '=' means TRUE */
  425.         flag = TRUE;
  426.         processAssignment(intrinsicIndex);
  427.     }
  428.     else /* Get the value to set to, either as a string, a
  429.             numeric value, or a boolean flag */
  430.         if (getAssignment( ( char * ) option + indx, &indx, intrinsicType[ intrinsicIndex ] ) != ERROR)
  431.             processAssignment( intrinsicIndex );
  432.     return(errCount ? -1 : 0);
  433. }
  434.  
  435. /* Process a config file */
  436. int processConfigFile( char *configFileName )
  437. {
  438.     FILE *configFilePtr;
  439.     int ch = 0, theCh;
  440.     int errType, errPos = 0, lineBufCount, intrinsicIndex;
  441.     int indx;
  442.     char inBuffer[ LINEBUF_SIZE ];
  443.  
  444.     line = 1;
  445.     errCount = 0;
  446.     errtag = "config.txt";
  447.  
  448.     if( ( configFilePtr = fopen( configFileName, FOPRTXT ) ) == NULL )
  449.     {
  450.         fprintf(stderr, "Cannot open configuration file %s\n", configFileName );
  451.         return( OK );    /* treat like empty config file */
  452.     }
  453.  
  454.     /* Process each line in the configFile */
  455.     while( ch != EOF )
  456.     {
  457.         /* Skip whitespace */
  458.         while( ( ( ch = getc( configFilePtr ) ) == ' ' || ch == '\t' ) && ch != EOF )
  459.         ;
  460.  
  461.         /* Get a line into the inBuffer */
  462.         hasError = FALSE;
  463.         lineBufCount = 0;
  464.         errType = NO_ERROR;
  465.         while( ch != '\r' && ch != '\n' && ch != CPM_EOF && ch != EOF )
  466.         {
  467.             /* Check for an illegal char in the data */
  468.             if( ( ch < ' ' || ch > '~' ) && ch != '\r' && ch != '\n' &&
  469.                 ch != ' ' && ch != '\t' && ch != CPM_EOF && ch != EOF )
  470.             {
  471.                 if( errType == NO_ERROR )
  472.                     /* Save position of first illegal char */
  473.                     errPos = lineBufCount;
  474.                 errType = ILLEGAL_CHAR_ERROR;
  475.             }
  476.  
  477.             /* Make sure the path is of the correct length.  Note that the
  478.                code is ordered so that a LINELENGTH_ERROR takes precedence over
  479.                an ILLEGAL_CHAR_ERROR */
  480.             if( lineBufCount > LINEBUF_SIZE )
  481.                 errType = LINELENGTH_ERROR;
  482.             else
  483.                 inBuffer[ lineBufCount++ ] = ch;
  484.  
  485.             if( ( ch = getc( configFilePtr ) ) == '#' )
  486.             {
  487.                 /* Skip comment section and trailing whitespace */
  488.                 while( ch != '\r' && ch != '\n' && ch != CPM_EOF && ch != EOF )
  489.                     ch = getc( configFilePtr );
  490.                 break;
  491.             }
  492.         }
  493.  
  494.         /* Skip trailing whitespace and add der terminador */
  495.         while(lineBufCount && (( theCh = inBuffer[ lineBufCount - 1 ] ) == ' ' || theCh == '\t' ))
  496.             lineBufCount--;
  497.  
  498.         inBuffer[ lineBufCount ] = '\0';
  499.  
  500.         /* Process the line unless its a blank or comment line */
  501.         if( lineBufCount && *inBuffer != '#' )
  502.         {
  503.             switch( errType )
  504.             {
  505.                 case LINELENGTH_ERROR:
  506.                     fprintf(stderr, "%s: line '%.30s...' too long\n", errtag, inBuffer );
  507.                     errCount++;
  508.                     break;
  509.  
  510.                 case ILLEGAL_CHAR_ERROR:
  511.                     fprintf(stderr, "> %s\n  ", inBuffer );
  512.                     fprintf(stderr, "%*s^\n", errPos, ""); 
  513.                     fprintf(stderr, "%s: bad character in command on line %d\n", errtag, line );
  514.                     errCount++;
  515.                     break;
  516.  
  517.                 default:
  518.                     for( indx = 0;
  519.                          indx < LINEBUF_SIZE && ( ch = inBuffer[ indx ] ) != '\0'
  520.                                 && ch != ' ' && ch != '\t' && ch != '=';
  521.                          indx++ )
  522.                              ;
  523.                     if( ( intrinsicIndex = lookup( inBuffer, indx, intrinsics, CONFIG_INTRINSICS ) ) == ERROR )
  524.                     {
  525.                         errCount++;
  526.                     }
  527.                     else
  528.                     {
  529.                         /* Get the value to set to, either as a string, a
  530.                            numeric value, or a boolean flag */
  531.                         getAssignment( inBuffer + indx, &indx, intrinsicType[ intrinsicIndex ] );
  532.                         processAssignment( intrinsicIndex );
  533.                     }
  534.             }
  535.         }
  536.  
  537.         /* Handle special-case of ^Z if configFile came off an MSDOS system */
  538.         if( ch == CPM_EOF )
  539.             ch = EOF;
  540.  
  541.         /* Exit if there are too many errors */
  542.         if( errCount >= MAX_ERRORS )
  543.             break;
  544.  
  545.         line++;
  546.     }
  547.  
  548.     fclose( configFilePtr );
  549.  
  550.     /* Exit if there were errors */
  551.     if( errCount )
  552.     {
  553.         fprintf(stderr, "%s: %s%d error(s) detected\n\n", configFileName, ( errCount >= MAX_ERRORS ) ?
  554.                 "Maximum level of " : "", errCount );
  555.         return( ERROR );
  556.     }
  557.  
  558.     return( OK );
  559. }
  560.